//
//  BufferedFile.c
//  3DImportTest
//
//  Created by David Michaeli on 26/08/12.
//  Copyright (c) 2012 David Michaeli. All rights reserved.
//

#include <stdio.h>
#include <sys/stat.h>
#include <memory.h>
#include <stdlib.h>

#include "BufferedFile.h"

//==========================================================
BufferedFile* OpenFile (char *fname, char *flags)
{
    // try to create the file buffer
    BufferedFile* file = (BufferedFile*)malloc(sizeof(BufferedFile));
    if (file==NULL)
        return NULL;
    
    stat(fname, &file->statistics);
 
#ifdef WIN32
    file->actual_block_size = 8192;
#else
    file->actual_block_size = file->statistics.st_blksize;
#endif
    if (file->actual_block_size == 0)
        file->actual_block_size = 8192;
    
    if ( file->actual_block_size < 1024 )
        file->actual_block_size *= (8192/file->actual_block_size);
    
    
    file->buffer = (char*)malloc(file->actual_block_size);
    if (file->buffer==NULL)
    {
        free (file);
        return NULL;
    }
    
    // try opening the file
    file->fid = fopen(fname, flags);
    if (file->fid == NULL)
    {
        free(file->buffer);
        free(file);
        return NULL;
    }
    
    // read the file buffer
    file->current_buffer_size = fread(file->buffer, 1, file->actual_block_size, file->fid);
    file->cur_pos = 0;
    
    return file;
}

//==========================================================
void CloseFile (BufferedFile* file)
{
    if (file==NULL)
        return;
    
    if (file->fid!=NULL)
        fclose (file->fid);
    
    if (file->buffer != NULL)
        free (file->buffer);
    
    free(file);
}

//==========================================================
unsigned int ReadData(BufferedFile* file, unsigned int size, char* buffer)
{
    unsigned int left_to_copy = size;
    unsigned int current_available = 0;
    unsigned int current_read_size = 0;
    unsigned int offset = 0;
    
    
    while (left_to_copy)
    {
        current_available = (unsigned int)(file->current_buffer_size-file->cur_pos);
        current_read_size = current_available;
        
        if (left_to_copy <= current_available)
        {
            current_read_size = left_to_copy;
        }
        
        memcpy(buffer+offset, file->buffer+file->cur_pos, current_read_size);
        file->cur_pos += current_read_size;
        
        offset += current_read_size;
        left_to_copy -= current_read_size;
        
        if (file->cur_pos == (file->current_buffer_size-1))
        {
            file->current_buffer_size = fread(file->buffer, 1, file->actual_block_size, file->fid);
            file->cur_pos = 0;
            
            if (file->current_buffer_size==0)
            {
                break;
            }
        }
    }
    
    return offset;
}

//==========================================================
unsigned int WriteData(BufferedFile* file, unsigned int size, char* buffer)
{
    unsigned int left_to_write = size;
    unsigned int current_write = 0;
    unsigned int offset = 0;
    
    while (left_to_write)
    {
        current_write = left_to_write;
        
        if (current_write > file->actual_block_size)
        {
            current_write = (unsigned int)file->actual_block_size;
        }
        
        fwrite(buffer+offset, 1, current_write, file->fid);
        
        offset += current_write;
        left_to_write -= current_write;
        
    }
    
    return offset;
}

//==========================================================
char* ReadLineFromFile (BufferedFile* file, char* line, unsigned int max_size)
{
    unsigned int left_to_read = max_size;
    unsigned int pos = 0;
    unsigned long left_char_in_buffer = 0;
    unsigned int already_finished = 0;
    
    while (left_to_read)
    {
        left_char_in_buffer = (unsigned int)(file->current_buffer_size - file->cur_pos);
        
        if (left_char_in_buffer==0)
        {
            left_char_in_buffer = file->current_buffer_size = fread(file->buffer, 1, file->actual_block_size, file->fid);
            file->cur_pos = 0;
            
            // end of file
            if (left_char_in_buffer == 0 )
            {
                if (pos==0) return NULL;
                
                line[pos] = '\0';
                return line;
            }
        }
        
        while (left_char_in_buffer)
        {
            if (file->buffer[file->cur_pos] == '\n' ||
                file->buffer[file->cur_pos] == '\r' ||
                file->buffer[file->cur_pos] == '\0' )
            {
                if (!already_finished)
                {
                    line[pos] = '\0';
                    already_finished = 1;
                }
                
                file->cur_pos ++;
                left_char_in_buffer--;
                left_to_read--;
            }
            else
            {
                if (already_finished)
                {
                    return line;
                }
                
                line[pos++] = file->buffer[file->cur_pos++];
                left_char_in_buffer--;
                left_to_read--;
            }
        }
    }
    
    return line;
}

//==========================================================
int WriteLineIntoFile (BufferedFile* file, char* line)
{
    return fputs(line, file->fid);
}

